/**
 *  Copyright 2006-2007 EmbedChat
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.embedchat.desktop.gui;

import org.embedchat.desktop.Observer;
import org.embedchat.desktop.StateModel;
import org.embedchat.protocol.message.server.UsersListMessage.UserDescription;
import org.javabb.bbcode.BBCodeProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.text.Document;
import javax.swing.text.html.HTMLEditorKit;

import java.awt.*;
import java.awt.event.*;
import java.io.StringReader;
import java.util.List;

/**
 * 
 * @author koba
 *
 */
public class ChatGUI extends Observer implements DocumentListener {

    private static final Logger log = LoggerFactory.getLogger(ChatGUI.class);

    static {
        try {
        	String lookAndFeel = UIManager.getSystemLookAndFeelClassName();
            UIManager.setLookAndFeel(lookAndFeel);
        } catch (Exception e) {
            log.error("Bad look and Feel: ", e);
        }
    }
    
    private StateModel model;
    private JFrame frame = new JFrame("Embedded chat - 0.1");
    private JTextField userName = new JTextField();
    private JPasswordField userPass = new JPasswordField();
    private DefaultTableModel usersTableModel = new DefaultTableModel();
    private StringBuffer messagesBuff = new StringBuffer();
    private final JEditorPane messagesList = new JEditorPane("html", "");
    private final JEditorPane area = new JEditorPane("html", "");
    private RoomListComboModel roomListModel = new RoomListComboModel();
    private JComboBox roomsList = new JComboBox(roomListModel);
    private JMenuItem logoutMenuItem = new JMenuItem("Logout");    
    private JButton connectionButton = new JButton("Connection...");    
    private JTable usersList;

    private JPanel chatArea = getChatArea();
    private JPanel connectSideBar = getConnectSideBar();
    private JPanel loginSideBar = getLoginSidebar();
    private JPanel userList = getUserListArea();
    private JPanel sendText = getSendTextArea();
    
	private SmilesHandler smilesHandler = new SmilesHandler();
	private BBCodeProcessor bbCodeProcessor = new BBCodeProcessor();

    private static final int SIDEBAR_HEIGHT = 350;
    private static final int SIDEBAR_WIDTH = 220;
    private static final int CHAT_AREA_WIDHT = 350;

    private int currentState = 0;
    private static final int IN_LOGIN_FORM = 1;
    private static final int IN_CHAT_FORM = 2;
    private static final int IN_RECONNECT_FORM = 3;

    public static final String CONNECT_ACTION_COMMAND = "Login";
    private JSplitPane pane;

    public ChatGUI(StateModel model) {
        this.model = model;
    }

    public void createAndShowGUI() {
        log.debug("createAndShowGUI()");
        
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.addWindowListener(new WindowListener() {
            public void windowOpened(WindowEvent arg0) {
            }

            public void windowClosing(WindowEvent arg0) {
                log.debug("disconnecting");
                model.setRequestForDisconnect(true);
                model.update();
            }

            public void windowClosed(WindowEvent arg0) {
            }

            public void windowIconified(WindowEvent arg0) {
            }

            public void windowDeiconified(WindowEvent arg0) {
            }

            public void windowActivated(WindowEvent arg0) {

            }

            public void windowDeactivated(WindowEvent arg0) {
            }
        });
        messagesList.setEditable(false);

        messagesList.setEditorKit(new MyHTMLEditorKit());
        messagesList.setContentType("text/html");
        //messagesList.setContentType("text/html; charset=UTF-8");
        
        roomsList.setEditable(false);
        JPanel mainPanel = new JPanel(new BorderLayout());
        mainPanel.add(connectSideBar, BorderLayout.NORTH);
        mainPanel.add(loginSideBar, BorderLayout.WEST);
        pane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
        JSplitPane chatPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
        chatPane.add(chatArea);
        chatPane.add(sendText);
        chatPane.setResizeWeight(1.0);
        pane.add(chatPane);
        pane.add(userList);
        pane.setResizeWeight(1.0);
        pane.setDividerLocation(450);
        pane.setVisible(false);
        mainPanel.add(pane, BorderLayout.CENTER);
        JMenuBar menuBar = new JMenuBar();
        JMenu menu = new JMenu("File");
        JMenuItem preferencesMenuItem = new JMenuItem("Preferences");
        preferencesMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                OptionsDialog dialog = new OptionsDialog();
                dialog.setInitValues(model.getConnectionHost(), model.getConnectionPort());
                dialog.pack();
                SwingUtils.centerComponent(dialog);
                dialog.setVisible(true);
                if ((model.getConnectionHost().equals(dialog.getServerHost()))
                        && (model.getConnectionPort() == dialog.getServerPort())) {
                    return;
                }
                model.setConnectionParameters(
                        dialog.getServerHost(),
                        dialog.getServerPort()
                );
                connectionButton.setText("Trying to reconnect");
                currentState = IN_RECONNECT_FORM;
                connectSideBar.repaint();
                connectSideBar.setVisible(true);
                loginSideBar.setVisible(false);
                pane.setVisible(false);
                frame.pack();
                frame.repaint();
                log.debug("REQUEST FOR RECONNECT");
                model.setRequestForReconnect(true);
            }
        });

        logoutMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
            	model.setRequestForReconnect(true);
            	model.update();
            }
        });
        JMenuItem exitMenuItem = new JMenuItem("Exit");
        exitMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                model.setRequestForDisconnect(true);
                model.update();
                for (int i = 0; i < 10; i++) {
                    if (StateModel.CONNECTION_ESTABLISHED_STATE == model.getModelState()) {
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            log.error(e.getMessage(), e);
                        }
                    }
                }
                frame.dispose();
            }
        });
        menu.add(preferencesMenuItem);
        menu.add(logoutMenuItem);
        menu.add(exitMenuItem);
        menuBar.add(menu);
        frame.setJMenuBar(menuBar);

        frame.getContentPane().add(mainPanel);
        loginSideBar.setVisible(false);
        pane.setVisible(false);
        frame.pack();
        SwingUtils.centerComponent(frame);
        frame.setVisible(true);
    }

    private JPanel getConnectSideBar() {
        JPanel panel = new JPanel(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();
        c.fill = GridBagConstraints.BOTH;
        c.anchor = GridBagConstraints.FIRST_LINE_START;
        c.insets = new Insets(2, 2, 0, 0);
        panel.setPreferredSize(new Dimension(SIDEBAR_WIDTH, SIDEBAR_HEIGHT));
        panel.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(Color.BLACK),
                BorderFactory.createEmptyBorder(5, 5, 5, 5)));
//        c.gridx = 0;
//        c.gridy = 0;
//        panel.add(connectionLabel, c);
        connectionButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
            	connectionButton.setText("Connection...");
                model.setRequestForReconnect(true);
                model.update();
            }
        });
        c.gridx = 0;
        c.gridy = 0;
        panel.add(connectionButton, c);
        return panel;
    }

    private JPanel getLoginSidebar() {
        JPanel panel = new JPanel(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();
        c.fill = GridBagConstraints.BOTH;
        c.anchor = GridBagConstraints.FIRST_LINE_START;
        c.insets = new Insets(2, 2, 0, 0);
        panel.setPreferredSize(new Dimension(SIDEBAR_WIDTH, SIDEBAR_HEIGHT));
        panel.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(Color.BLACK),
                BorderFactory.createEmptyBorder(5, 5, 5, 5)));
        userName.getDocument().addDocumentListener(this);
        userName.setPreferredSize(new Dimension(140, 25));
        userPass.getDocument().addDocumentListener(this);
        userPass.setPreferredSize(new Dimension(140, 25));
        JLabel nameLabel = new JLabel("login:");
        JLabel passLabel = new JLabel("passw:");
        JLabel roomLabel = new JLabel("room:");
        c.gridx = 0;
        c.gridy = 0;
        panel.add(roomLabel, c);
        c.gridx = 1;
        c.gridy = 0;
        panel.add(roomsList, c);
        c.gridx = 0;
        c.gridy = 1;
        panel.add(nameLabel, c);
        c.gridx = 1;
        c.gridy = 1;
        panel.add(userName, c);
        c.gridx = 0;
        c.gridy = 2;
        panel.add(passLabel, c);
        c.gridx = 1;
        c.gridy = 2;
        panel.add(userPass, c);
        JButton button = new JButton(CONNECT_ACTION_COMMAND);
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
            	model.setSelectedRoomId(roomsList.getSelectedIndex());
                model.setRequestForLogin(true);
                model.update();
            }
        });
        c.gridx = 1;
        c.gridy = 3;
        panel.add(button, c);
        return panel;
    }

    private JPanel getChatArea() {
        JPanel panel = new JPanel(new GridLayout(1, 0));
        panel.setPreferredSize(new Dimension(CHAT_AREA_WIDHT, SIDEBAR_HEIGHT));
        panel.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(Color.BLACK),
                BorderFactory.createEmptyBorder()));
        JScrollPane scrollPane = new JScrollPane(messagesList);
        scrollPane.setDoubleBuffered(true);
        scrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() {
            public void adjustmentValueChanged(AdjustmentEvent e) {
                messagesList.select(messagesList.getDocument().getLength() + 1000, 0);
            }
        });
        scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        panel.add(scrollPane);
        return panel;
    }

    private JPanel getUserListArea() {
        JPanel panel = new JPanel(new GridLayout(1, 0));
        panel.setPreferredSize(new Dimension(SIDEBAR_WIDTH, SIDEBAR_HEIGHT));
        panel.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(Color.BLACK),
                BorderFactory.createEmptyBorder()));
        log.debug("User list: " + userList);
        usersTableModel.addColumn("Users list");
        usersList = new JTable(usersTableModel);
        usersList.setEnabled(false);
        panel.add(usersList);
        return panel;
    }

    private JPanel getSendTextArea() {
        JPanel panel = new JPanel(new GridLayout(1, 0));
        panel.setPreferredSize(new Dimension(300, 40));
        panel.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(Color.BLACK),
                BorderFactory.createEmptyBorder()));
        JScrollPane scrollPane = new JScrollPane(area);
        scrollPane.setDoubleBuffered(true);
        scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);        
        area.addKeyListener(new KeyListener() {
            public void keyPressed(KeyEvent event) {
            }

            public void keyReleased(KeyEvent event) {
            }

            public void keyTyped(KeyEvent event) {
                if (event.getKeyChar() == '\n') {
                    if (area.getText().trim().length() > 0) {
                        model.setSendMessage(area.getText().trim());
                        model.setRequestForSendMessage(true);
                        area.setText("");
                        area.repaint();
                    }
                }
            }

        });
        panel.add(scrollPane);
        return panel;
    }

    private void showError(String message) {
        JOptionPane.showMessageDialog(frame, message);
    }

    @Override
    public void update() {
        log.debug("UPDATE");
        if (StateModel.CONNECTION_ESTABLISHED_STATE == model.getModelState()) {
            if ((currentState != IN_LOGIN_FORM && currentState != IN_CHAT_FORM)) {
                currentState = IN_LOGIN_FORM;
                log.debug("show login form");
                connectSideBar.setVisible(false);
                logoutMenuItem.setEnabled(false);
                if (model.getRooms().size() > 0) {
                    String[] names = new String[model.getRooms().size()];                	
                    for (Integer roomMapId : model.getRooms().keySet()) {
                    	names[roomMapId] = model.getRooms().get(roomMapId).getRoomName();
                    	log.debug("Try to add: " + model.getRooms().get(roomMapId).getRoomName());
                    	roomsList.insertItemAt(model.getRooms().get(roomMapId).getRoomName(), roomMapId);
    				}
                    //roomsList = new JComboBox(names);
                    roomsList.setSelectedIndex(0);
                    pane.setVisible(false);
                    loginSideBar.setVisible(true);
                } else {
                	showError("There are not active rooms on this server");
                	model.setRequestForDisconnect(true);
                }
                frame.pack();
            }
        }
        
        if (StateModel.DISCONNECTED_STATE == model.getModelState()) {
            if ((currentState == IN_CHAT_FORM) || (currentState == IN_LOGIN_FORM)) {
                currentState = IN_RECONNECT_FORM;
                log.debug("show login form");
                pane.setVisible(false);
                connectionButton.setText("Connection lost");
                loginSideBar.setVisible(false);
                connectSideBar.setVisible(true);
                logoutMenuItem.setEnabled(false);
                frame.pack();
            }
        }

        if (StateModel.USER_LOGGED_STATE == model.getModelState()) {
            if (currentState != IN_CHAT_FORM) {
                logoutMenuItem.setEnabled(true);
                currentState = IN_CHAT_FORM;
                log.debug("show chat application");
                connectSideBar.setVisible(false);
                loginSideBar.setVisible(false);
                messagesBuff.setLength(0);
                messagesList.setText("");
                pane.setVisible(true);
                frame.pack();
            }

            usersTableModel.getDataVector().removeAllElements();
            usersList.repaint();

            List<UserDescription> currentUsersList = model.getRoomUsers();
            for (UserDescription description : currentUsersList) {
                usersTableModel.addRow(new Object[]{description.getUserName()});
            }
        }

        if (model.isRequestForError()) {
            if (connectSideBar.isVisible()) {
            	connectionButton.setText(model.getErrorMessage());
            } else {
                showError(model.getErrorMessage());
            }
            model.setRequestForError(false);
        }

        if (model.isRequestForShowMessage()) {
            model.setRequestForShowMessage(false);
            try {
            	HTMLEditorKit kit = (HTMLEditorKit) messagesList.getEditorKit();
            	Document doc = messagesList.getDocument();
            	String messageStr = bbCodeProcessor.preparePostText(model.getReceivedMessage());
            	messageStr = smilesHandler.transformString(messageStr);
            	
            	String message = "<b>" + model.getReceivedMessageAuthor()+ "</b>" + ": " +  messageStr;
            	String resultMessage = "<span style=\"font-family:Verdana; font-size:10px;\">" + message + "</span>";
            	log.debug("message: " + message);
            	StringReader reader = new StringReader(resultMessage);
            	kit.read(reader, doc, doc.getLength());
			} catch (Exception e) {
				log.error(e.getMessage(), e);
			}
        }
        log.debug("COMPLETE");
    }

    public void insertUpdate(DocumentEvent event) {
        model.setLoginName(userName.getText());
        model.setPassword(String.valueOf(userPass.getPassword()));
    }

    public void removeUpdate(DocumentEvent event) {
        log.debug("removeUpdate: " + event);

    }

    public void changedUpdate(DocumentEvent event) {
        log.debug("changedUpdate: " + event);

    }

}
